package name.huzhenbo.java.algorithm.talent;

import java.util.*;

/**
 * http://www.javaeye.com/topic/505538
 */
class ConsumptionSettlement {
    private List<String> members;

    // some of the members are partners, they share the pay
    private Map<String, String> groups;

    private List<Consumption> records;
    private Map<String, Float> consumptions;

    public ConsumptionSettlement() {
        records = new ArrayList<Consumption>();
        consumptions = new HashMap<String, Float>();
    }

    public void setMembers(List<String> members) {
        this.members = members;
        for (String member : this.members) {
            consumptions.put(member, new Float(0));
        }
    }


    public void setGroups(Map<String, String> groups) {
        this.groups = groups;
    }

    public void record(int amount, int holder, int[] sharers) {
        records.add(new Consumption(amount, holder, sharers));
    }

    public void go() {
        for (Consumption record : records) {
            inject(record);
        }
        group();
        distribute();
    }

    private void distribute() {
        List<Pay> pays = new ArrayList<Pay>();
        for(Map.Entry<String, Float> entry: consumptions.entrySet()){
            pays.add(new Pay(entry.getKey(), entry.getValue()));
        }
        Collections.sort(pays);
        for(Pay pay: pays){
            System.out.println(pay);
        }
    }

    private void group() {
        List<String> toBeDels = new ArrayList<String>();
        for (String member : members) {
            String group = groups.get(member);
            if (group == null) continue;
            if (consumptions.containsKey(group)) {
                consumptions.put(group, consumptions.get(group) + consumptions.get(member));
            } else {
                consumptions.put(group, consumptions.get(member));
            }
            toBeDels.add(member);
        }
        for(String toDel: toBeDels){
            consumptions.remove(toDel);
        }
    }

    private void inject(Consumption record) {
        int shareNum = record.sharers.length;
        for (int i = 0; i < shareNum; i++) {
            int memberIndex = record.sharers[i];
            String member = members.get(memberIndex);
            if (memberIndex == record.creditor) {
                consumptions.put(member, consumptions.get(member) + record.amount * (shareNum - 1) / shareNum);
            } else {
                consumptions.put(member, consumptions.get(member) - record.amount / shareNum);
            }
        }
    }

    private class Consumption {
        private float amount;
        private int creditor;
        private int[] sharers;

        public Consumption(float amount, int creditor, int[] sharers) {
            this.amount = amount;
            this.creditor = creditor;
            this.sharers = sharers;
        }
    }

    private class Pay implements Comparable<Pay>{
        private String member;
        private Float amount;

        public Pay(String member, Float amount) {
            this.member = member;
            this.amount = amount;
        }

        public int compareTo(Pay another) {
            return amount.compareTo((another).amount);
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(member);
            sb.append(amount > 0 ? " receive " :  " pay ");
            sb.append(Math.round(Math.abs(amount)));
            return sb.toString();
        }
    }
}
